package com.contract.management.bean.page.datatables;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.alibaba.fastjson.JSON;
import com.contract.management.bean.page.PageBean;
import com.contract.management.bean.page.QueryBean;
import com.contract.management.util.StrUtils;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 接收DataTables提交的参数
 * https://datatables.net/manual/server-side
 * 
 * @author zhangxl
 *
 */
public class DtsParams {
	
	private int draw;
	
	private int start;
	
	private int length;

	//封装DataTables提交的排序信息,提交信息如下：
	//order[0][column] 0, order[0][dir] desc
	//保存为 [{column:0,dir:"desc"}] 
	private List<Map<String, String>> order;
	
	//查询信息,客户端采用"JSON.stringify($("#searchForm").serializeArray())"方法提交，格式如下：
	//query [{"name":"username","value":"李"}, {"name":"startAge","value": "20"}, {"name":"endAge","value": "50"}]
	//转换为Map格式。{"username": "李", "startAge": "20", "endAge": "50"}
	private Map<String, String> query = new LinkedHashMap<>();
	
	public int getDraw() {
		return draw;
	}

	public void setDraw(int draw) {
		this.draw = draw;
	}

	public int getStart() {
		return start;
	}

	public void setStart(int start) {
		this.start = start;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public List<Map<String, String>> getOrder() {
		return order;
	}

	public void setOrder(List<Map<String, String>> order) {
		this.order = order;
	}

	/**
	 * @param column 为columns 数组中位置
	 * @param dir	为"desc" 降序 或 "asc" 升序
	 */
//	public void setOrder(String column, String dir) {
//		if(this.order == null) {
//			this.order = new HashMap<String, String>();
//		}
//		this.order.put(column, dir);
//	}

	public Map<String, String> getQuery() {
		return query;
	}

	/**
	 * 获取客户端采用"JSON.stringify($("#searchForm").serializeArray())"方法提交的查询信息，如：
	 * <pre>
	 * data: function(d){
	 *     d.query = JSON.stringify($("#search_form").serializeArray());
	 * }
	 * </pre>
	 * @param query 格式："[{\"name\": \"username\", \"value\" : \"李\"}, {\"name\":\"startAge\",\"value\": \"20\"}, {\"name\":\"endAge\",\"value\": \"50\"}]"
	 */
	public void setQuery(String query) {
		if(StrUtils.isEmpty(query)) return;
		ObjectMapper mapper = new ObjectMapper();
		try {
			List<Map<String, String>> list = mapper.readValue(query, List.class);
			if(list == null || list.size() == 0) return;
			this.query = new LinkedHashMap<>();
			for (Map<String, String> map : list) {
				this.query.put(map.get("name"), map.get("value"));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public PageBean createQuery(String[] columns, String domain, Function<Map<String, String>, List<QueryBean>> where) {
		PageBean pb = new PageBean();
		Map<String, Object> params = new HashMap<>();
		String selectClause = createSelect(columns, domain);
		String whereClause = createWhere(params, where);
		String orderClause = createOrder(columns);
		pb.setQl(selectClause + whereClause + orderClause);
		if(length > 0) {
			String sumQl = "select count(*) from " + domain + " obj";
			pb.setSumQl(sumQl + whereClause);
		}
		pb.setParams(params);
		return pb;
	}
	
	public PageBean createQuery(String[] columns, String ql, String sumQl, Function<Map<String, String>, List<QueryBean>> where) {
		PageBean pb = new PageBean();
		Map<String, Object> params = new HashMap<>();
		String whereClause = createWhere(params, where);
		String orderClause = createOrder(columns);
		pb.setQl(ql + whereClause + orderClause);
		if(length > 0) {
			pb.setSumQl(sumQl + whereClause);
		}
		pb.setParams(params);
		return pb;
	}
	
	private String createSelect(String[] columns, String domain) {
		StringBuilder sb = new StringBuilder("select ");
		for(int i = 0; i < columns.length; i++) {
			if(i != 0) sb.append(", ");
			sb.append("obj.").append(columns[i]);
		}
		sb.append(" from ").append(domain).append(" obj");
		return sb.toString();
	}
	
	private String createWhere(Map<String, Object> params, Function<Map<String, String>, List<QueryBean>> where) {
		if(where == null) return "";

		List<QueryBean> queryList = where.apply(query);
		if(queryList == null || queryList.size() == 0) return "";

		StringBuilder whereSb = new StringBuilder(" where");
		for(int i = 0; i < queryList.size(); i++) {
			QueryBean q = queryList.get(i);
			if(i != 0) whereSb.append(" ").append(q.getConn());
			
			whereSb.append(" ").append(q.getClause("obj"));
			
			if(q.getParam() != null) params.put(q.getNamedParam(), q.getParam());
		}
		return whereSb.toString();
	}
	
	private String createOrder(String[] columns) {
		if(order == null || order.size() == 0) return "";
		StringBuilder orderSb = new StringBuilder();
		for(Map<String, String> map : this.order) {
			String name = columns[Integer.parseInt(map.get("column"))];
			orderSb.append(orderSb.length() == 0 ? " order by" : ",");
			orderSb.append(" obj.").append(name).append(" ").append(map.get("dir"));
		}
		return orderSb.toString();
	}
	
	public static void main(String[] args) {
		DtsParams params = new DtsParams();
		params.setLength(10);
		params.setQuery("[{\"name\": \"username\", \"value\" : \"李\"}, {\"name\":\"startAge\",\"value\": \"20\"}, {\"name\":\"endAge\",\"value\": \"50\"}]");
		
		PageBean pb = params.createQuery(new String[]{"id", "name", "age", "sex"}, "User", (query) -> {
			List<QueryBean> ret = new ArrayList<>();
			for(Map.Entry<String, String> entry : query.entrySet()) {
				if(StrUtils.isEmpty(entry.getValue())) continue;
				String key = entry.getKey();
				if("username".equals(key)) {
					ret.add(new QueryBean("name", "like", "%" + entry.getValue() + "%").setLeftParen("("));
					ret.add(new QueryBean("addr", "like").setNamedParam("name").setConn("or").setRightParen(")"));
				} else if("startAge".equals(key)) {
					ret.add(new QueryBean("age.sex", "in").setValue("(select id from Address where id in (:hello))").setParam("aaaa").setNamedParam("hello"));
				}
			}
			return ret;
		});
		System.out.println(JSON.toJSONString(pb, true));
	}
}
